/**
 * @file market_order_book.h
 * @brief 市场订单簿类的定义
 * 
 * @details 该文件定义了市场订单簿类，用于维护限价订单簿的状态。
 * 实现了高效的订单添加、修改、取消和成交处理逻辑，
 * 并提供了最佳买卖盘口（BBO）的维护和查询功能。
 * 该类使用内存池来高效管理订单和价格级别对象。
 * 
 * @author 原作者
 * @date 2023
 */

#pragma once

#include "common/types.h"
#include "common/mem_pool.h"
#include "common/logging.h"

#include "market_order.h"
#include "exchange/market_data/market_update.h"

/**
 * @namespace Trading
 * @brief 交易相关的命名空间
 * 
 * @details 包含交易系统的核心组件，如订单簿、交易引擎、交易策略等。
 */
namespace Trading {
  class TradeEngine;

  /**
   * @class MarketOrderBook
   * @brief 市场订单簿类
   * 
   * @details 该类实现了一个高效的限价订单簿，用于维护市场中的订单状态。
   * 主要功能包括：
   * 1. 处理市场数据更新（添加、修改、取消订单和成交）
   * 2. 维护最佳买卖盘口（BBO）信息
   * 3. 提供订单簿状态的字符串表示
   * 
   * 该类使用内存池来高效管理订单和价格级别对象，
   * 并使用哈希表和双向链表来实现快速的订单查找和价格级别管理。
   */
  class MarketOrderBook final {
  public:
    /**
     * @brief 构造函数
     * 
     * @details 初始化市场订单簿对象，设置证券ID和日志记录器，
     * 并初始化内存池和其他数据结构。
     * 
     * @param ticker_id 证券标识符，用于唯一标识该订单簿所对应的证券
     * @param logger 日志记录器指针，用于记录订单簿的操作和状态变化
     */
    MarketOrderBook(TickerId ticker_id, Logger *logger);

    /**
     * @brief 析构函数
     * 
     * @details 清理订单簿资源，包括记录最终状态到日志，
     * 并释放相关指针和内存。
     */
    ~MarketOrderBook();

    /**
     * @brief 处理市场数据更新并更新限价订单簿
     * 
     * @details 根据市场更新类型（添加、修改、取消、成交等）处理订单簿的变化，
     * 并在必要时更新最佳买卖盘口（BBO）信息。处理完成后，
     * 会通知交易引擎订单簿已更新。
     * 
     * @param market_update 市场更新数据指针，包含更新类型、订单ID、侧向、价格和数量等信息
     */
    auto onMarketUpdate(const Exchange::MEMarketUpdate *market_update) noexcept -> void;

    /**
     * @brief 设置交易引擎指针
     * 
     * @details 将订单簿与交易引擎关联，使订单簿可以在状态变化时
     * 通知交易引擎。
     * 
     * @param trade_engine 交易引擎指针
     */
    auto setTradeEngine(TradeEngine *trade_engine) {
      trade_engine_ = trade_engine;
    }

    /**
     * @brief 更新最佳买卖盘口（BBO）信息
     * 
     * @details 根据当前订单簿状态更新最佳买卖盘口信息，包括最佳买入价格、
     * 数量和最佳卖出价格、数量。可以选择只更新买入侧、卖出侧或同时更新两侧。
     * 
     * @param update_bid 是否更新买入侧（bid）的最佳价格和数量
     * @param update_ask 是否更新卖出侧（ask）的最佳价格和数量
     */
    auto updateBBO(bool update_bid, bool update_ask) noexcept {
      if(update_bid) {
        if(bids_by_price_) {
          bbo_.bid_price_ = bids_by_price_->price_;
          bbo_.bid_qty_ = bids_by_price_->first_mkt_order_->qty_;
          for(auto order = bids_by_price_->first_mkt_order_->next_order_; order != bids_by_price_->first_mkt_order_; order = order->next_order_)
            bbo_.bid_qty_ += order->qty_;
        }
        else {
          bbo_.bid_price_ = Price_INVALID;
          bbo_.bid_qty_ = Qty_INVALID;
        }
      }

      if(update_ask) {
        if(asks_by_price_) {
          bbo_.ask_price_ = asks_by_price_->price_;
          bbo_.ask_qty_ = asks_by_price_->first_mkt_order_->qty_;
          for(auto order = asks_by_price_->first_mkt_order_->next_order_; order != asks_by_price_->first_mkt_order_; order = order->next_order_)
            bbo_.ask_qty_ += order->qty_;
        }
        else {
          bbo_.ask_price_ = Price_INVALID;
          bbo_.ask_qty_ = Qty_INVALID;
        }
      }
    }

    /**
     * @brief 获取当前最佳买卖盘口（BBO）信息
     * 
     * @details 返回当前订单簿的最佳买卖盘口信息，包括最佳买入价格、
     * 数量和最佳卖出价格、数量。
     * 
     * @return 指向当前 BBO 对象的指针
     */
    auto getBBO() const noexcept -> const BBO* {
      return &bbo_;
    }

    /**
     * @brief 将订单簿状态转换为字符串表示
     * 
     * @details 生成订单簿当前状态的文本表示，包括买入和卖出侧的所有价格级别。
     * 可以选择是否显示详细信息（如每个订单的详细信息）和是否进行有效性检查。
     * 
     * @param detailed 是否显示详细信息，如果为 true，将显示每个订单的详细信息
     * @param validity_check 是否进行有效性检查，如果为 true，将检查价格级别是否按照正确的顺序排列
     * @return 订单簿状态的字符串表示
     */
    auto toString(bool detailed, bool validity_check) const -> std::string;

    /**
     * @brief 删除的构造函数和赋值运算符
     * 
     * @details 为了防止订单簿对象的意外复制和赋值，显式删除了以下函数：
     * - 默认构造函数：订单簿必须指定证券ID和日志记录器
     * - 复制构造函数：防止意外复制订单簿对象
     * - 移动构造函数：防止意外移动订单簿对象
     * - 复制赋值运算符：防止意外复制赋值
     * - 移动赋值运算符：防止意外移动赋值
     */
    MarketOrderBook() = delete;

    /**
     * @brief 删除的复制构造函数
     */
    MarketOrderBook(const MarketOrderBook &) = delete;

    /**
     * @brief 删除的移动构造函数
     */
    MarketOrderBook(const MarketOrderBook &&) = delete;

    /**
     * @brief 删除的复制赋值运算符
     */
    MarketOrderBook &operator=(const MarketOrderBook &) = delete;

    /**
     * @brief 删除的移动赋值运算符
     */
    MarketOrderBook &operator=(const MarketOrderBook &&) = delete;

  private:
    /**
     * @brief 证券标识符
     * 
     * @details 唯一标识该订单簿所对应的证券，在构造时指定且不可更改。
     */
    const TickerId ticker_id_;

    /**
     * @brief 交易引擎指针
     * 
     * @details 指向拥有此限价订单簿的父交易引擎，用于在订单簿变化或成交发生时
     * 发送通知。通过 setTradeEngine 方法设置。
     */
    TradeEngine *trade_engine_ = nullptr;

    /**
     * @brief 订单ID到市场订单的映射
     * 
     * @details 哈希表，用于快速根据订单ID查找对应的市场订单对象。
     * 这使得订单的修改和取消操作可以高效地进行。
     */
    OrderHashMap oid_to_order_;

    /**
     * @brief 价格级别对象的内存池
     * 
     * @details 用于管理 MarketOrdersAtPrice 对象的内存池，提高内存分配和释放的效率。
     * 在构造函数中初始化，大小为 ME_MAX_PRICE_LEVELS。
     */
    MemPool<MarketOrdersAtPrice> orders_at_price_pool_;

    /**
     * @brief 买入和卖出侧最佳价格级别的指针
     * 
     * @details 指向买入和卖出侧最佳价格级别（盘口顶部）的指针。
     * bids_by_price_ 指向买入侧最高价格级别，
     * asks_by_price_ 指向卖出侧最低价格级别。
     */
    MarketOrdersAtPrice *bids_by_price_ = nullptr;
    MarketOrdersAtPrice *asks_by_price_ = nullptr;

    /**
     * @brief 价格到价格级别对象的映射
     * 
     * @details 哈希表，用于快速根据价格查找对应的价格级别对象。
     * 这使得在特定价格上的订单操作可以高效地进行。
     */
    OrdersAtPriceHashMap price_orders_at_price_;

    /**
     * @brief 市场订单对象的内存池
     * 
     * @details 用于管理 MarketOrder 对象的内存池，提高内存分配和释放的效率。
     * 在构造函数中初始化，大小为 ME_MAX_ORDER_IDS。
     */
    MemPool<MarketOrder> order_pool_;

    /**
     * @brief 最佳买卖盘口（BBO）信息
     * 
     * @details 存储当前订单簿的最佳买卖盘口信息，包括最佳买入价格、
     * 数量和最佳卖出价格、数量。通过 updateBBO 方法更新，
     * 可以通过 getBBO 方法获取。
     */
    BBO bbo_;

    /**
     * @brief 时间字符串缓存
     * 
     * @details 用于日志记录的时间字符串缓存，避免重复创建时间字符串。
     */
    std::string time_str_;
    
    /**
     * @brief 日志记录器指针
     * 
     * @details 用于记录订单簿操作和状态变化的日志记录器指针。
     * 在构造函数中初始化。
     */
    Logger *logger_ = nullptr;

  private:
    /**
     * @brief 将价格转换为映射索引
     * 
     * @details 将给定的价格转换为价格映射表中的索引。使用取模运算来确保索引
     * 在有效范围内（小于 ME_MAX_PRICE_LEVELS）。
     * 
     * @param price 要转换的价格
     * @return 对应的映射表索引
     */
    auto priceToIndex(Price price) const noexcept {
      return (price % ME_MAX_PRICE_LEVELS);
    }

    /**
     * @brief 获取特定价格的订单集合
     * 
     * @details 根据给定的价格，获取并返回对应的 MarketOrdersAtPrice 对象。
     * 该对象包含在该价格级别上的所有订单。
     * 
     * @param price 要查找的价格
     * @return 对应价格的 MarketOrdersAtPrice 指针，如果该价格不存在订单，则返回 nullptr
     */
    auto getOrdersAtPrice(Price price) const noexcept -> MarketOrdersAtPrice * {
      return price_orders_at_price_.at(priceToIndex(price));
    }

    /**
     * @brief 添加新的价格级别到容器中
     * 
     * @details 将新的价格级别对象添加到容器中，包括哈希表和价格级别的双向链表。
     * 该方法会根据价格和侧向（买入或卖出）将新的价格级别插入到正确的位置，
     * 以维持价格级别的正确排序（买入侧按价格降序，卖出侧按价格升序）。
     * 
     * @param new_orders_at_price 要添加的新价格级别对象指针
     */
    auto addOrdersAtPrice(MarketOrdersAtPrice *new_orders_at_price) noexcept {
      price_orders_at_price_.at(priceToIndex(new_orders_at_price->price_)) = new_orders_at_price;

      const auto best_orders_by_price = (new_orders_at_price->side_ == Side::BUY ? bids_by_price_ : asks_by_price_);
      if (UNLIKELY(!best_orders_by_price)) {
        (new_orders_at_price->side_ == Side::BUY ? bids_by_price_ : asks_by_price_) = new_orders_at_price;
        new_orders_at_price->prev_entry_ = new_orders_at_price->next_entry_ = new_orders_at_price;
      } else {
        auto target = best_orders_by_price;
        bool add_after = ((new_orders_at_price->side_ == Side::SELL && new_orders_at_price->price_ > target->price_) ||
                          (new_orders_at_price->side_ == Side::BUY && new_orders_at_price->price_ < target->price_));
        if (add_after) {
          target = target->next_entry_;
          add_after = ((new_orders_at_price->side_ == Side::SELL && new_orders_at_price->price_ > target->price_) ||
                       (new_orders_at_price->side_ == Side::BUY && new_orders_at_price->price_ < target->price_));
        }
        while (add_after && target != best_orders_by_price) {
          add_after = ((new_orders_at_price->side_ == Side::SELL && new_orders_at_price->price_ > target->price_) ||
                       (new_orders_at_price->side_ == Side::BUY && new_orders_at_price->price_ < target->price_));
          if (add_after)
            target = target->next_entry_;
        }

        if (add_after) { // add new_orders_at_price after target.
          if (target == best_orders_by_price) {
            target = best_orders_by_price->prev_entry_;
          }
          new_orders_at_price->prev_entry_ = target;
          target->next_entry_->prev_entry_ = new_orders_at_price;
          new_orders_at_price->next_entry_ = target->next_entry_;
          target->next_entry_ = new_orders_at_price;
        } else { // add new_orders_at_price before target.
          new_orders_at_price->prev_entry_ = target->prev_entry_;
          new_orders_at_price->next_entry_ = target;
          target->prev_entry_->next_entry_ = new_orders_at_price;
          target->prev_entry_ = new_orders_at_price;

          if ((new_orders_at_price->side_ == Side::BUY && new_orders_at_price->price_ > best_orders_by_price->price_) ||
              (new_orders_at_price->side_ == Side::SELL &&
               new_orders_at_price->price_ < best_orders_by_price->price_)) {
            target->next_entry_ = (target->next_entry_ == best_orders_by_price ? new_orders_at_price
                                                                               : target->next_entry_);
            (new_orders_at_price->side_ == Side::BUY ? bids_by_price_ : asks_by_price_) = new_orders_at_price;
          }
        }
      }
    }

    /**
     * @brief 从容器中移除价格级别
     * 
     * @details 从容器中移除指定侧向和价格的价格级别对象，包括从哈希表和价格级别的
     * 双向链表中移除。如果被移除的价格级别是当前最佳价格（盘口顶部），
     * 则会相应地更新 bids_by_price_ 或 asks_by_price_ 指针。
     * 
     * @param side 要移除的价格级别的侧向（买入或卖出）
     * @param price 要移除的价格级别的价格
     */
    auto removeOrdersAtPrice(Side side, Price price) noexcept {
      const auto best_orders_by_price = (side == Side::BUY ? bids_by_price_ : asks_by_price_);
      auto orders_at_price = getOrdersAtPrice(price);

      if (UNLIKELY(orders_at_price->next_entry_ == orders_at_price)) { // empty side of book.
        (side == Side::BUY ? bids_by_price_ : asks_by_price_) = nullptr;
      } else {
        orders_at_price->prev_entry_->next_entry_ = orders_at_price->next_entry_;
        orders_at_price->next_entry_->prev_entry_ = orders_at_price->prev_entry_;

        if (orders_at_price == best_orders_by_price) {
          (side == Side::BUY ? bids_by_price_ : asks_by_price_) = orders_at_price->next_entry_;
        }

        orders_at_price->prev_entry_ = orders_at_price->next_entry_ = nullptr;
      }

      price_orders_at_price_.at(priceToIndex(price)) = nullptr;

      orders_at_price_pool_.deallocate(orders_at_price);
    }

    /**
     * @brief 从容器中移除并释放指定的订单
     * 
     * @details 从容器中移除指定的订单对象，并释放其内存。如果该订单是其价格级别上的
     * 唯一订单，则会移除整个价格级别。否则，会从订单链表中移除该订单，并在必要时
     * 更新价格级别的第一个订单指针。
     * 
     * @param order 要移除的订单对象指针
     */
    auto removeOrder(MarketOrder *order) noexcept -> void {
      auto orders_at_price = getOrdersAtPrice(order->price_);

      if (order->prev_order_ == order) { // only one element.
        removeOrdersAtPrice(order->side_, order->price_);
      } else { // remove the link.
        const auto order_before = order->prev_order_;
        const auto order_after = order->next_order_;
        order_before->next_order_ = order_after;
        order_after->prev_order_ = order_before;

        if (orders_at_price->first_mkt_order_ == order) {
          orders_at_price->first_mkt_order_ = order_after;
        }

        order->prev_order_ = order->next_order_ = nullptr;
      }

      oid_to_order_.at(order->order_id_) = nullptr;
      order_pool_.deallocate(order);
    }

    /**
     * @brief 在相应价格级别的FIFO队列中添加单个订单
     * 
     * @details 将指定的订单添加到其对应价格级别的FIFO（先进先出）队列中。
     * 如果该价格级别不存在，则创建新的价格级别并将订单添加为第一个订单。
     * 如果该价格级别已存在，则将订单添加到队列的末尾。
     * 
     * @param order 要添加的订单对象指针
     */
    auto addOrder(MarketOrder *order) noexcept -> void {
      const auto orders_at_price = getOrdersAtPrice(order->price_);

      if (!orders_at_price) {
        order->next_order_ = order->prev_order_ = order;

        auto new_orders_at_price = orders_at_price_pool_.allocate(order->side_, order->price_, order, nullptr, nullptr);
        addOrdersAtPrice(new_orders_at_price);
      } else {
        auto first_order = (orders_at_price ? orders_at_price->first_mkt_order_ : nullptr);

        first_order->prev_order_->next_order_ = order;
        order->prev_order_ = first_order->prev_order_;
        order->next_order_ = first_order;
        first_order->prev_order_ = order;
      }

      oid_to_order_.at(order->order_id_) = order;
    }
  };

  /**
   * @brief 证券ID到市场订单簿的映射
   * 
   * @details 使用固定大小的数组实现的哈希表，用于快速根据证券ID查找对应的市场订单簿对象。
   * 数组大小由 ME_MAX_TICKERS 常量决定，表示系统支持的最大证券数量。
   */
  typedef std::array<MarketOrderBook *, ME_MAX_TICKERS> MarketOrderBookHashMap;
}
